/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pl.put.dcs_modeler.computingObjectEditor;

import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Logger;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.openide.cookies.SaveCookie;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.windows.CloneableTopComponent;
import org.openide.windows.TopComponent;
//import org.openide.util.Utilities;
import pl.put.dcs.core.utils.serializer.SerializeManager;
import pl.put.dcs_modeler.exceptions.DCSModelerException;
import pl.put.dcs_modeler.fileTypeDataLoader.dataObjects.DCSComputingObjectDataObject;
import pl.put.dcs_modeler.loaders.ComputingObjectLoader;
import pl.put.dcs_modeler.utils.DCSProjectUtil;
import pl.put.dcs_modeler.utils.JarUtil;
import pl.put.model.computingObject.ComputingObject;
import pl.put.model.computingObject.pin.Pin;
import pl.put.model.computingObject.pin.PinIn;
import pl.put.model.computingObject.pin.PinOut;

/**
 * Top component which displays something.
 */
public class ComputingObjectEditorTopComponent extends CloneableTopComponent implements ActionListener, ChangeListener
{

    private static Logger log = Logger.getLogger( ComputingObjectEditorTopComponent.class.getName() );
    private static ComputingObjectEditorTopComponent instance;
    private ComputingObject computingObj;
    /** path to the icon used by the component and its open action */
//    static final String ICON_PATH = "SET/PATH/TO/ICON/HERE";
    private static final String PREFERRED_ID = "ComputingObjectEditorTopComponent";
    private Project project;
    private DummyNode saveNode;
    DCSComputingObjectDataObject dataObject;

    public ComputingObjectEditorTopComponent( DCSComputingObjectDataObject dc )
    {
        this.project = FileOwnerQuery.getOwner( dc.getPrimaryFile() );
        initComponents();
        setName( NbBundle.getMessage( ComputingObjectEditorTopComponent.class, "CTL_ComputingObjectEditorTopComponent" ) );
        setToolTipText( NbBundle.getMessage( ComputingObjectEditorTopComponent.class, "HINT_ComputingObjectEditorTopComponent" ) );
//        setIcon(Utilities.loadImage(ICON_PATH, true));
        ComputingObject co = null;
        this.dataObject = dc;
        try
        {
            InputStream is = dc.getPrimaryFile().getInputStream();
            ComputingObjectLoader loader = new ComputingObjectLoader();
            co = loader.getComputingObjectFromFile( is );
           
            is.close();
        }
        catch ( IOException ex )
        {
            Exceptions.printStackTrace( ex );
        }
        
        this.computingObj = co;
        initTables();
        loadComputingObject( co );
        setActivatedNodes( new Node[]
                {
                    saveNode = new DummyNode()
                } );
        saveNode.fire( false );

    }

    private ComputingObjectEditorTopComponent()
    {
        //throw new UnsupportedOperationException( "Not yet implemented" );
    }

    public void save() throws IOException
    {
        saveNode.save();
    }

    private void initTables()
    {
        //tabela 1 - in
        int vColIndex = 1;
        TableColumn col = inPinsTable.getColumnModel().getColumn( vColIndex );
        String[] values = getDataModelClasses();
        col.setCellEditor( new DataTypeEditor( values ) );
        col.setCellRenderer( new DataTypeRenderBox( values ) );
        // tabela nr2 (out)
        col = outPinsTable.getColumnModel().getColumn( vColIndex );
        values = getDataModelClasses();
        col.setCellEditor( new DataTypeEditor( values ) );
        col.setCellRenderer( new DataTypeRenderBox( values ) );

    }

    private String[] getDataModelClasses()
    {
        String dmPath = DCSProjectUtil.getDataModelPath( project );
        
        List<String> ret = JarUtil.getJarClassList( dmPath );
        return ret.toArray( new String[ 0 ] );
    }

    private void loadComputingObject( ComputingObject co )
    {
        List< PinIn > pinInList = co.getPinInList();
        List< PinOut > pinOutList = co.getPinOutList();
        if ( pinInList != null )
        {
            for ( Pin pin : pinInList )
            {
                addPinToTable( this.inPinsTable, pin.getName(), pin.getType() );
            }
        }
        if ( pinOutList != null )
        {
            for ( Pin pin : pinOutList )
            {
                addPinToTable( this.outPinsTable, pin.getName(), pin.getType() );
            }
        }
    }

    private void addPinToTable( JTable table, String pinName, String pinType )
    {
        Vector< String > el = new Vector< String >();
        el.add( pinName );
        el.add( pinType );
        ( ( DefaultTableModel )table.getModel() ).addRow( el );
    }

    private void delPin( JTable table )
    {
        ( ( DefaultTableModel )table.getModel() ).removeRow( table.getSelectedRow() );
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents()
    {

        jLabel1 = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        inPinsTable = new javax.swing.JTable();
        jLabel2 = new javax.swing.JLabel();
        jScrollPane2 = new javax.swing.JScrollPane();
        outPinsTable = new javax.swing.JTable();
        deleteInButton = new javax.swing.JButton();
        addInPinButton = new javax.swing.JButton();
        deleteOutPinButton = new javax.swing.JButton();
        addOutPinButton = new javax.swing.JButton();

        org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(ComputingObjectEditorTopComponent.class, "ComputingObjectEditorTopComponent.jLabel1.text")); // NOI18N

        inPinsTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][]
            {

            },
            new String []
            {
                "Name", "Type"
            }
        )
        {
            Class[] types = new Class []
            {
                java.lang.String.class, java.lang.String.class
            };

            public Class getColumnClass(int columnIndex)
            {
                return types [columnIndex];
            }
        });
        inPinsTable.addMouseListener(new java.awt.event.MouseAdapter()
        {
            public void mouseClicked(java.awt.event.MouseEvent evt)
            {
                inPinsTableMouseClicked(evt);
            }
        });
        jScrollPane1.setViewportView(inPinsTable);
        inPinsTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(ComputingObjectEditorTopComponent.class, "ComputingObjectEditorTopComponent.inPinsTable.columnModel.title0")); // NOI18N
        inPinsTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(ComputingObjectEditorTopComponent.class, "ComputingObjectEditorTopComponent.inPinsTable.columnModel.title1")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(jLabel2, org.openide.util.NbBundle.getMessage(ComputingObjectEditorTopComponent.class, "ComputingObjectEditorTopComponent.jLabel2.text")); // NOI18N

        outPinsTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][]
            {

            },
            new String []
            {
                "Name", "Type"
            }
        )
        {
            Class[] types = new Class []
            {
                java.lang.String.class, java.lang.String.class
            };

            public Class getColumnClass(int columnIndex)
            {
                return types [columnIndex];
            }
        });
        outPinsTable.addMouseListener(new java.awt.event.MouseAdapter()
        {
            public void mouseClicked(java.awt.event.MouseEvent evt)
            {
                outPinsTableMouseClicked(evt);
            }
        });
        jScrollPane2.setViewportView(outPinsTable);
        outPinsTable.getColumnModel().getColumn(0).setHeaderValue(org.openide.util.NbBundle.getMessage(ComputingObjectEditorTopComponent.class, "ComputingObjectEditorTopComponent.outPinsTable.columnModel.title0")); // NOI18N
        outPinsTable.getColumnModel().getColumn(1).setHeaderValue(org.openide.util.NbBundle.getMessage(ComputingObjectEditorTopComponent.class, "ComputingObjectEditorTopComponent.outPinsTable.columnModel.title1")); // NOI18N

        org.openide.awt.Mnemonics.setLocalizedText(deleteInButton, org.openide.util.NbBundle.getMessage(ComputingObjectEditorTopComponent.class, "ComputingObjectEditorTopComponent.deleteInButton.text")); // NOI18N
        deleteInButton.addActionListener(new java.awt.event.ActionListener()
        {
            public void actionPerformed(java.awt.event.ActionEvent evt)
            {
                deleteInButtonActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(addInPinButton, org.openide.util.NbBundle.getMessage(ComputingObjectEditorTopComponent.class, "ComputingObjectEditorTopComponent.addInPinButton.text")); // NOI18N
        addInPinButton.addActionListener(new java.awt.event.ActionListener()
        {
            public void actionPerformed(java.awt.event.ActionEvent evt)
            {
                addInPinButtonActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(deleteOutPinButton, org.openide.util.NbBundle.getMessage(ComputingObjectEditorTopComponent.class, "ComputingObjectEditorTopComponent.deleteOutPinButton.text")); // NOI18N
        deleteOutPinButton.addActionListener(new java.awt.event.ActionListener()
        {
            public void actionPerformed(java.awt.event.ActionEvent evt)
            {
                deleteOutPinButtonActionPerformed(evt);
            }
        });

        org.openide.awt.Mnemonics.setLocalizedText(addOutPinButton, org.openide.util.NbBundle.getMessage(ComputingObjectEditorTopComponent.class, "ComputingObjectEditorTopComponent.addOutPinButton.text")); // NOI18N
        addOutPinButton.addActionListener(new java.awt.event.ActionListener()
        {
            public void actionPerformed(java.awt.event.ActionEvent evt)
            {
                addOutPinButtonActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jScrollPane2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 796, Short.MAX_VALUE)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addComponent(addInPinButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(deleteInButton))
                    .addComponent(jLabel2)
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                        .addComponent(addOutPinButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(deleteOutPinButton))
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 796, Short.MAX_VALUE)
                    .addComponent(jLabel1))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 240, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(deleteInButton)
                            .addComponent(addInPinButton)))
                    .addGroup(layout.createSequentialGroup()
                        .addGap(31, 31, 31)
                        .addComponent(jLabel2)))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 246, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(deleteOutPinButton)
                    .addComponent(addOutPinButton))
                .addGap(162, 162, 162))
        );
    }// </editor-fold>//GEN-END:initComponents

    private void deleteOutPinButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_deleteOutPinButtonActionPerformed
    {//GEN-HEADEREND:event_deleteOutPinButtonActionPerformed
        delPin( this.inPinsTable );
        this.saveNode.fire( true );
}//GEN-LAST:event_deleteOutPinButtonActionPerformed

    private void addOutPinButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_addOutPinButtonActionPerformed
    {//GEN-HEADEREND:event_addOutPinButtonActionPerformed
        addPinToTable( this.outPinsTable, "", "" );
        this.saveNode.fire( true );
}//GEN-LAST:event_addOutPinButtonActionPerformed

    private void addInPinButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_addInPinButtonActionPerformed
    {//GEN-HEADEREND:event_addInPinButtonActionPerformed
        addPinToTable( this.inPinsTable, "", "" );
        this.saveNode.fire( true );
    }//GEN-LAST:event_addInPinButtonActionPerformed

    private void deleteInButtonActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_deleteInButtonActionPerformed
    {//GEN-HEADEREND:event_deleteInButtonActionPerformed
        delPin( this.outPinsTable );
        this.saveNode.fire( true );
    }//GEN-LAST:event_deleteInButtonActionPerformed

    private void inPinsTableMouseClicked(java.awt.event.MouseEvent evt)//GEN-FIRST:event_inPinsTableMouseClicked
    {//GEN-HEADEREND:event_inPinsTableMouseClicked
        this.saveNode.fire( true );
    }//GEN-LAST:event_inPinsTableMouseClicked

    private void outPinsTableMouseClicked(java.awt.event.MouseEvent evt)//GEN-FIRST:event_outPinsTableMouseClicked
    {//GEN-HEADEREND:event_outPinsTableMouseClicked
        this.saveNode.fire( true );
    }//GEN-LAST:event_outPinsTableMouseClicked

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton addInPinButton;
    private javax.swing.JButton addOutPinButton;
    private javax.swing.JButton deleteInButton;
    private javax.swing.JButton deleteOutPinButton;
    private javax.swing.JTable inPinsTable;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JTable outPinsTable;
    // End of variables declaration//GEN-END:variables

    /**
     * Gets default instance. Do not use directly: reserved for *.settings files only,
     * i.e. deserialization routines; otherwise you could get a non-deserialized instance.
     * To obtain the singleton instance, use {@link #findInstance}.
     */
    public static synchronized ComputingObjectEditorTopComponent getDefault()
    {
        if ( instance == null )
        {
            instance = new ComputingObjectEditorTopComponent();
        }
        return instance;
    }

    public boolean isBelongToThisTopComponent( DCSComputingObjectDataObject dCSComputingObjectDataObject )
    {
        if ( this.dataObject.equals( dCSComputingObjectDataObject ) )
        {
            return true;
        }
        return false;
    }

    /**
     * Obtain the ComputingObjectEditorTopComponent instance. Never call {@link #getDefault} directly!
     */
    public static synchronized ComputingObjectEditorTopComponent findInstance( DCSComputingObjectDataObject dCSComputingObjectDataObject )
    {
        if ( dCSComputingObjectDataObject == null )
        {
            return null;
        }
        Set< TopComponent > tcs = TopComponent.getRegistry().getOpened();
        for ( TopComponent tc : tcs )
        {
            if ( ( tc instanceof ComputingObjectEditorTopComponent ) && ( ( ( ComputingObjectEditorTopComponent ) tc ).isBelongToThisTopComponent( dCSComputingObjectDataObject ) ) )
            {
                return ( ComputingObjectEditorTopComponent )tc;
            }
        }
        return new ComputingObjectEditorTopComponent( dCSComputingObjectDataObject );
    }

    @Override
    public int getPersistenceType()
    {
        return TopComponent.PERSISTENCE_ALWAYS;
    }

    @Override
    public void componentOpened()
    {
        // TODO add custom code on component opening
    }

    @Override
    public void componentClosed()
    {
        // TODO add custom code on component closing
    }

    /** replaces this in object stream */
    @Override
    public Object writeReplace()
    {
        return new ResolvableHelper();
    }

    @Override
    protected String preferredID()
    {
        return PREFERRED_ID;
    }

    @Override
    public void actionPerformed( ActionEvent e )
    {
        throw new UnsupportedOperationException( "Not supported yet." );
    }

    @Override
    public void stateChanged( ChangeEvent e )
    {
        throw new UnsupportedOperationException( "Not supported yet." );
    }

    final static class ResolvableHelper implements Serializable
    {

        private static final long serialVersionUID = 1L;

        public Object readResolve()
        {
            return ComputingObjectEditorTopComponent.getDefault();
        }
    }

    private class DataTypeEditor extends DefaultCellEditor
    {

        public DataTypeEditor( String[] dataTypesArray )
        {
            super( new JComboBox( dataTypesArray ) );
        }
    }

    private class DataTypeRenderBox extends JComboBox implements TableCellRenderer
    {

        public DataTypeRenderBox( String[] items )
        {
            super( items );

        }

        @Override
        public Component getTableCellRendererComponent( JTable table, Object value,
                                                        boolean isSelected, boolean hasFocus, int row, int column )
        {
            if ( isSelected )
            {
                setForeground( table.getSelectionForeground() );
                super.setBackground( table.getSelectionBackground() );
            }
            else
            {
                setForeground( table.getForeground() );
                setBackground( table.getBackground() );
            }
            log.info( ( String )value );
            setSelectedItem( ( String )value );
            return this;
        }
    }
    
    private List< PinIn > getPinInListFromTable( JTable table )
    {
        Vector< Vector > pins = (Vector< Vector >)( ( DefaultTableModel )table.getModel() ).getDataVector();
        List< PinIn > pinList = new ArrayList< PinIn >();
        for ( Vector tmp : pins )
        {
            PinIn pin = new PinIn( ( String )tmp.elementAt( 0 ), ( String )tmp.elementAt( 1 ) );
            log.info( ( String )tmp.elementAt( 0 ) );
            pinList.add( pin );
        }
        return pinList;
    }
    
    private List< PinOut > getPinOutListFromTable( JTable table )
    {
        Vector< Vector > pins = ( ( DefaultTableModel )table.getModel() ).getDataVector();
        List< PinOut > pinList = new ArrayList< PinOut >();
        for ( Vector tmp : pins )
        {
            PinOut pin = new PinOut( ( String )tmp.elementAt( 0 ), ( String )tmp.elementAt( 1 ) );
            log.info( ( String )tmp.elementAt( 0 ) );
            pinList.add( pin );
        }
        return pinList;
    }
    
    private void inPinsTableToComputingObject()
    {
        computingObj.setPinInList( getPinInListFromTable( this.inPinsTable ) );
        computingObj.setPinOutList( getPinOutListFromTable( this.outPinsTable ) ); 
    }

    private class DummyNode extends AbstractNode
    {

        private boolean changed = false;
        SaveCookieImpl impl;

        public DummyNode()
        {

            super(Children.LEAF);
            impl = new SaveCookieImpl();

        }
        
        

        public void fire( boolean modified )
        {
            
                //Otherwise, we make no assignment
                //and the SaveCookie is not made available:
                if ( modified )
                {
                    getCookieSet().assign( SaveCookie.class, impl );
                    dataObject.setModified( true );
                }
                else
                {
                    getCookieSet().assign( SaveCookie.class );
                    dataObject.setModified( false );
                }
            
        }

        
        public void save()
        {
            try
            {
                impl.save();
            }
            catch ( IOException ex )
            {

                new DCSModelerException( "Computing object editor", "Error while saving." );
                Exceptions.printStackTrace( ex );
            }
        }
    

        private class SaveCookieImpl implements SaveCookie
        {

            @Override
            public void save() throws IOException
            {
                inPinsTableToComputingObject();
                SerializeManager sm = new SerializeManager();
                //sm.init();
                OutputStream os = dataObject.getPrimaryFile().getOutputStream();
                sm.serializeToFile( computingObj, os );
                os.close();
                fire( false );

            }
        }
    }
}
